package cn.xjfcloud.onedriveserver.service;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import com.fasterxml.jackson.databind.ObjectMapper;

import cn.xjfcloud.onedriveserver.dao.OnedriveFileInfoMapper;
import cn.xjfcloud.onedriveserver.dao.UploadInfoMapper;
import cn.xjfcloud.onedriveserver.pojo.InitConfig;
import cn.xjfcloud.onedriveserver.pojo.dto.ApiConfig;
import cn.xjfcloud.onedriveserver.pojo.dto.OnedriveFileInfo;
import cn.xjfcloud.onedriveserver.pojo.dto.UploadInfo;
import cn.xjfcloud.onedriveserver.util.ApiUtil;
import cn.xjfcloud.onedriveserver.util.UploadFileThread;
import cn.xjfcloud.onedriveserver.util.UploadFileUtil;

@Service
public class UploadFileService {
	@Autowired
	private UploadInfoMapper uploadInfoMapper;
	@Autowired
	private OnedriveFileInfoMapper onedriveFileInfoMapper;
	@Autowired
	private InitConfig initConfig;
	@Autowired
	private ApiConfigService apiConfigService;
	
	private static final Logger LOG = LoggerFactory.getLogger(UploadFileService.class);
	private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
	
	/**
	 * 上传文件
	 * @param path
	 * @param uploadSession
	 * @return
	 * @throws IOException
	 */
	public String uploadFile(String path,String uploadId) throws IOException{
		UploadInfo uploadInfo = getUploadInfoByUploadId(uploadId);
		File file = new File(path);
		if(file.isDirectory() || !file.exists()) {
			throw new FileNotFoundException(path + " is a directory or not exists" );
		}
		UploadFileUtil.getUploadFileThreadPoolExecutor().execute(new UploadFileThread(uploadInfo, this, path));
		return uploadId;
	} 
	
	public UploadInfo getUploadInfoByUploadId(String uploadId) {
		UploadInfo uploadInfo = uploadInfoMapper.getUploadInfoByUploadId(uploadId);
		return uploadInfo;
	}
	
	/**
	 * 获取上传的偏移量
	 * @param uploadId
	 * @return
	 */
	public Long getOffset(String uploadId) {
		UploadInfo uploadInfo = uploadInfoMapper.getUploadInfoByUploadId(uploadId);
		return uploadInfo.getOffset();
	}
	
	/**
	 * 更新offset
	 * @param uplouploadFileServiceadId
	 * @param offset
	 */
	public void updateOffset(String uploadId, Long offset) {
		UploadInfo uploadInfo = new UploadInfo();
		uploadInfo.setUploadId(uploadId);
		uploadInfo.setOffset(offset);
		uploadInfoMapper.updateUploadInfo(uploadInfo);
	}
	
	public Integer addUploadInfo(UploadInfo uploadInfo) {
		Integer count = uploadInfoMapper.addUploadInfo(uploadInfo);
		return count;
	}
	
	public Integer addOnedriveFileInfo(OnedriveFileInfo onedriveFileInfo) {
		Integer count = onedriveFileInfoMapper.addOnedriveFileInfo(onedriveFileInfo);
		return count;
	}
	
	public OnedriveFileInfo getOnedriveFileInfoByUploadId(String uploadId) {
		OnedriveFileInfo onedriveFileInfo = onedriveFileInfoMapper.getOnedriveFileInfoByUploadId(uploadId);
		return onedriveFileInfo;
	}
	
	/**
	 * 更新onedriveFleInfinitConfigo
	 * @param onedriveFileInfo
	 * @return
	 */
	public Integer updateOnedriveFileInfo(OnedriveFileInfo onedriveFileInfo) {
		Integer count = onedriveFileInfoMapper.updateOnedriveFileInfo(onedriveFileInfo);
		return count;
	}
	
	/**
	 * 利用fileId获取onedriveFileInfo
	 * @param fileId
	 * @return
	 */
	public OnedriveFileInfo getOnedriveFileInfoByFileId(String fileId) {
		OnedriveFileInfo onedriveFileInfo = onedriveFileInfoMapper.getOnedriveFileInfoByFileId(fileId);
		return onedriveFileInfo;
	}
	
	/**
	 * 更新uploadInfo
	 * @param uploadInfo
	 * @return
	 */
	public Integer updateUploadInfo(UploadInfo uploadInfo) {
		Integer count = uploadInfoMapper.updateUploadInfo(uploadInfo);
		return count;
	}
	
	/**
	 * 获取上传到onedrive时的分片大小的倍数
	 * @return
	 */
	public Integer getUploadPartCount() {
		return initConfig.getUplaodPartCount();
	}
	
	/**
	 * 根据uploadId删除上传信息
	 * @param uplaodId
	 * @return
	 */
	public boolean deleteUploadInfo(String uploadId) {
		Integer count = uploadInfoMapper.deleteUploadInfoByUploadId(uploadId);
		if(count == 1) {
			return true;
		}else {
			return false;
		}
	}
	
	/**
	 * 根据id删除onedriveFileInfo
	 * @param id
	 * @return
	 */
	public boolean deleteOnedriveFileInfoById(Integer id) {
		Integer count = onedriveFileInfoMapper.deleteOndriveFileInfoById(id);
		if(count == 1) {
			return true;
		}else {
			return false;
		}
	}
	
	/**
	 * 取消上传
	 * @param fileId
	 * @return
	 */
	public boolean cancelUpload(String fileId){
		OnedriveFileInfo onedriveFileInfo = getOnedriveFileInfoByFileId(fileId);
		String uploadId = onedriveFileInfo.getUploadId();
		//判断是否上传完成
		if(onedriveFileInfo.getFileSize() == null) {
			//上传未完成
			boolean flag = UploadFileUtil.interruptRunningThread(uploadId);
			//判断是否结束上传进程成功
			if(flag == false) {
				LOG.error("uploadid=" + uploadId + ",取消文件上传失败，原因：上传线程不存在！");
				return false;
			}else{
				UploadInfo uploadInfo = getUploadInfoByUploadId(uploadId);
				ApiConfig apiConfig = apiConfigService.getApiConfigById(onedriveFileInfo.getApiConfigId());
				try {
					//取消上传任务
					int code = ApiUtil.cancelUpload(uploadInfo.getUploadSession(), apiConfig);
					LOG.info("取消任务,response code=" + code + "，fileId=" + onedriveFileInfo.getFileId()+", filename=" + onedriveFileInfo.getFileName() + ", uploadid=" + uploadId + ",上传链接为=" + uploadInfo.getUploadSession());
					if(code == 204 || code == 401) {
						//删除数据库信息
						deleteUploadInfo(uploadId);
						deleteOnedriveFileInfoById(onedriveFileInfo.getId());
						return true;
					}else {
						return false;
					}
				} catch (IOException e) {
					TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
					LOG.error("uploadId=" + uploadId + ",取消上传任务失败,原因：发生错误", e );
					return false;
				}//try--end
			}//if--end--判断是否结束上传进程成功
		}else {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			LOG.error("uploadid=" + uploadId + ",取消文件上传失败，原因：上传文件已完成！");
			return false;
		}
	}
	
	/**
	 * 删除onedrive文件
	 * @param fileId
	 * @return
	 */
	public boolean deleteOnedriveFile(String fileId) {
		OnedriveFileInfo onedriveFileInfo = getOnedriveFileInfoByFileId(fileId);
		String onedrivePath = onedriveFileInfo.getOnedrivePath();
		ApiConfig apiConfig = apiConfigService.getApiConfigById(onedriveFileInfo.getApiConfigId());
		try {
			Integer result = ApiUtil.deleteOnedriveFile(onedrivePath, apiConfig);
			if(result == 204) {
				deleteUploadInfo(onedriveFileInfo.getUploadId());
				deleteOnedriveFileInfoById(onedriveFileInfo.getId());
				return true;
			}else {
				return false;
			}
		} catch (IOException e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			LOG.error("删除文件失败，", e);
			return false;
		}
	}
	
	/**
	 * 获取所有正在上传的文件
	 * @return
	 */
	public List<Map<String, String>> getAllUploadingFileInfo(){
		List<Map<String, String>> allUploadingFileInfo = onedriveFileInfoMapper.getAllUploadingFileInfo();
		return allUploadingFileInfo;
	}
	
	/**
	 * 获取upload session
	 * @param filePath
	 * @param fileId
	 * @param apiConfigId
	 * @param isRetry 是否重试
	 * @return
	 * @throws IOException
	 */
	public Map<String, Object> getUploadSession(String filePath, String fileId, Integer apiConfigId, Boolean isRetry) throws IOException{
		ApiConfig apiConfig = apiConfigService.getApiConfigById(apiConfigId);
		String result = ApiUtil.getUploadSession(filePath, fileId, apiConfig);
		Map<String, Object> sessionMap = OBJECT_MAPPER.readValue(result, HashMap.class);
		if(null == sessionMap.get("error") || "".equals(sessionMap.get("error"))) {
			return sessionMap;
		}else {
			apiConfig = apiConfigService.getApiConfigByIdNoCache(apiConfigId);
			Date now = new Date();
			//判断数据库中的数据是否以被其他客户端更新
			if(now.getTime() - apiConfig.getGetAccessTokenTime().getTime() > apiConfig.getExpireTime() * 1000) {
				//没有被更新，数据库中也是过期，刷新token
				apiConfigService.getTokenByRefreshToken(apiConfigId);
			}
			if(isRetry == true) {
				return getUploadSession(filePath, fileId, apiConfigId, false);
			}else {
				return sessionMap;
			}//if---end 判断是否重试
		}//if---end 判断获取session是否成功
	}
	
}
